/**
 * Game.java
 *
 * Roslan's Remake of MOO in 3D using Ardor3D
 * (C) 2010-2012 Roslan Amir. All rights reserved.
 */

package name.roslan.games.moo3d;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;

import com.ardor3d.extension.ui.UIComponent;
import com.ardor3d.extension.ui.UIHud;
import com.ardor3d.framework.DisplaySettings;
import com.ardor3d.image.Texture;
import com.ardor3d.input.control.OrbitCamControl;
import com.ardor3d.intersection.PickData;
import com.ardor3d.intersection.Pickable;
import com.ardor3d.math.MathUtils;
import com.ardor3d.math.Vector3;
import com.ardor3d.renderer.Camera;
import com.ardor3d.renderer.Renderer;
import com.ardor3d.scenegraph.Node;
import com.ardor3d.scenegraph.extension.Skybox;
import com.ardor3d.util.ReadOnlyTimer;
import com.ardor3d.util.TextureManager;
import com.thoughtworks.xstream.XStream;

/**
 * The main game class. This class extends <code>GameBase</code> and controls the setup of Ardor3D stuff.
 * 
 * @author Roslan Amir
 * @version 1.0 - Dec 16, 2010
 */
public class Game extends GameBase {

    /**
     * Configuration settings file.
     */
    private static final String CONFIG_FILE = "settings.xml";

    /**
     * The image file to use as the background.
     */
    private static final String IMAGE_BACKGROUND_TEXTURE = "images/starfield3.jpg";

    /**
     * The instance of the UIHud class
     */
    public static UIHud hud;

    /**
     * Our orbiter control.
     */
    public static OrbitCamControl control;

    /**
     * The camera being used.
     */
    public static Camera camera;

    /**
     * The skybox instance.
     */
    private static Skybox skybox;

    /**
     * Start the program.
     * 
     * @param args
     */
    public static void main(String[] args) {
        initSettings();
        start(Game.class);
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.ardor3d.example.ExampleBase#registerInputTriggers()
     */
    @Override
    protected void registerInputTriggers() {
        super.registerInputTriggers();

        /*
         * Add camera handler - set it up to control the main camera.
         */
        addCameraHandler();

        /*
         * Register the "mouse moved" for actions.
         */
        GameManager.getInstance().registerMouseMove(_logicalLayer);

        /*
         * Register the key strokes used by this game.
         */
        GameManager.getInstance().registerKeys(_logicalLayer);
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.ardor3d.example.ExampleBase#initGame()
     */
    @Override
    protected void initGame() {
        _canvas.setTitle("Master of Orion 3D (Java - Ardor3D)");
//      _canvas.setVSyncEnabled(true);

        UIComponent.setUseTransparency(true);

        _lightState.setEnabled(true);

        /*
         * Set the background star field
         */
        final Node backgroundNode = new Node("BackgroundNode");
        skybox = new Skybox("Skybox", 500, 500, 500);
        final Texture texture = TextureManager.load(IMAGE_BACKGROUND_TEXTURE, Texture.MinificationFilter.Trilinear, true);
        skybox.setTexture(Skybox.Face.Up, texture);
        skybox.setTexture(Skybox.Face.South, texture);
        backgroundNode.attachChild(skybox);
        _root.attachChild(backgroundNode);

        hud = new UIHud();
        hud.setupInput(_canvas, _physicalLayer, _logicalLayer);
        camera = _canvas.getCanvasRenderer().getCamera();

        /*
         * Get the singleton instance of GameManager and initializes it.
         */
        GameManager.getInstance().initialize(this);
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.ardor3d.example.ExampleBase#updateLogicalLayer(com.ardor3d.util.ReadOnlyTimer)
     */
    @Override
    protected void updateLogicalLayer(ReadOnlyTimer timer) {
        hud.getLogicalLayer().checkTriggers(timer.getTimePerFrame());
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.ardor3d.example.ExampleBase#renderGame(com.ardor3d.renderer.Renderer)
     */
    @Override
    protected void renderGame(Renderer renderer) {
        super.renderGame(renderer);
        renderer.renderBuckets();
        renderer.draw(hud);
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.ardor3d.example.ExampleBase#updateGame(com.ardor3d.util.ReadOnlyTimer)
     */
    @Override
    protected void updateGame(ReadOnlyTimer timer) {
        GameManager.getInstance().updateGame(timer);

        /*
         * Don't know what this does -- need to investigate
         */
        control.update(timer.getTimePerFrame()); // required
        hud.updateGeometricState(timer.getTimePerFrame()); // does not seem required

        skybox.setTranslation(camera.getLocation());
        skybox.updateGeometricState(0.0f, true);
    }

    /**
     * A <code>Spatial</code> was picked. Now determine what to do with the fact.
     * 
     * @param pick
     * @param target
     */
    @Override
    protected void processPick(PickData pick, Pickable target) {
        GameManager.getInstance().processPick(pick, target);
    }

    /**
     * Initialize the Display Settings by reading it from the config file or using the defaults if the file is missing.
     */
    private static void initSettings() {
        StringBuilder sb = new StringBuilder();
        BufferedReader in = null;
        String line;
        try {
            XStream xstream = new XStream();
            xstream.alias("settings", DisplaySettings.class);
            in = new BufferedReader(new FileReader(CONFIG_FILE));
            while ((line = in.readLine()) != null) {
                line = line.trim();
                sb.append(line);
                // sb.append('\n');
            }
            settings = (DisplaySettings) xstream.fromXML(sb.toString());
        } catch (FileNotFoundException ex) {
            logger.warn("Configuration file " + CONFIG_FILE + " not found, using defaults");
            settings = new DisplaySettings(DEFAULT_SCREEN_WIDTH, DEFAULT_SCREEN_HEIGHT, DEFAULT_SCREEN_COLOR_DEPTH, DEFAULT_SCREEN_FREQUENCY,
                    DEFAULT_SCREEN_ALPHA_BITS, DEFAULT_SCREEN_DEPTH_BITS, DEFAULT_SCREEN_STENCIL_BITS, DEFAULT_SCREEN_SAMPLES, DEFAULT_SCREEN_FULLSCREEN,
                    DEFAULT_SCREEN_STEREO, null);
            XStream xstream = new XStream();
            xstream.alias("settings", DisplaySettings.class);
            String xml = xstream.toXML(settings);
            PrintWriter out = null;
            try {
                out = new PrintWriter(new BufferedWriter(new FileWriter(CONFIG_FILE)));
                out.print(xml);
            } catch (IOException ex1) {
                ex1.printStackTrace();
                System.exit(1);
            } finally {
                if (out != null)
                    out.close();
            }
        } catch (IOException ex) {
            logger.error("Unexpected exception when reading the configuration file");
            System.exit(1);
        } finally {
            if (in != null)
                try {
                    in.close();
                } catch (IOException ex) {
                }
        }

        GameManager.getInstance().screenWidth = settings.getWidth();
        GameManager.getInstance().screenHeight = settings.getHeight();
    }

    /**
     * Add Strategy handler - set it up to control the main camera.
     */
    private void addCameraHandler() {
        control = new OrbitCamControl(_canvas.getCanvasRenderer().getCamera(), Vector3.ZERO);
        control.setupMouseTriggers(_logicalLayer, true);
        control.setSphereCoords(20.0, 90 * MathUtils.DEG_TO_RAD, 0.0);
        control.setMaxAscent(-1.0);
        control.setMinAscent(-1.0);
        control.setMaxZoomDistance(50.0);
        control.setMinZoomDistance(5.0);
        control.setZoomSpeed(0.005);
        control.setBaseDistance(5.0);
    }

}
